/*
 * $Id: PRCMessageContainerFactory.java,v 1.5 2009/05/06 16:15:03 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor;

import java.util.ArrayList;
import java.util.List;

import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.naming.NamingException;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.Lifecycle;
import org.springframework.core.task.TaskExecutor;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.jndi.JndiTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.Assert;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.database.dao.IPrcToTaskCategoryDAO;
import com.ge.healthcare.autosc.common.database.pojo.CfgPrcToTaskCategory;
import com.ge.healthcare.autosc.common.dbLogger.StepLogger;
import com.ge.healthcare.autosc.processor.exception.PRCMessageContainerException;

/**
 * @author 212042764
 * 
 */
public class PRCMessageContainerFactory implements InitializingBean, Lifecycle {
	private IPrcToTaskCategoryDAO prcToTaskCategoryDAO = null;
	private JndiTemplate jndiTemplate = null;
	private PlatformTransactionManager transactionManager = null;
	private boolean sessionTransacted = false;
	private Object messageListener = null;
	private ConnectionFactory connectionFactory = null;
	private TaskExecutor taskExecutor = null;
	private List<DefaultMessageListenerContainer> messageContainersLst = new ArrayList<DefaultMessageListenerContainer>();
	private boolean isRunning = false;

	/**
	 * Spring likes me
	 */
	public PRCMessageContainerFactory() {
		super();
	}

	/**
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() {
		this.validate();
		// Need to start the initialization
		doInitialize();
	}

	/**
	 * Checks if the minimum data has been set
	 */
	private void validate() {
		Assert.notNull(taskExecutor, "Task Executor needs to be set");
		Assert.notNull(messageListener, "Message Listener needs to be set");
		Assert.notNull(connectionFactory, "Connection Factory needs to be set");
		Assert.notNull(jndiTemplate, "JNDI Template needs to be set");
		Assert.notNull(prcToTaskCategoryDAO,
				"PrcToTaskCategoryDAO needs to be set");
	}

	/**
	 * The trick we are trying to do is: <ol type="1."> <li>Get the managed
	 * server name from step logger</li> <li>Look up mapping for the task
	 * category based on managed server name</li> <li>The queue name can then be
	 * retrieved from the task category making everything being read from DB</li>
	 * </ol>
	 * 
	 * @see org.springframework.jms.listener.DefaultMessageListenerContainer#doInitialize()
	 */
	protected void doInitialize() {
		String methodName = "doInitialize";
		ASCLogger.info(this.getClass(), methodName, new StringBuffer().append(
				"Trying to initialize using managed server name:").append(
				StepLogger.getManagedServerName()));
		List<CfgPrcToTaskCategory> taskCategoryInfoLst = prcToTaskCategoryDAO
				.getPrcToTaskCategoryDetailsForServer(StepLogger
						.getManagedServerName());
		if (ASCUtil.isNullOrEmptyList(taskCategoryInfoLst)) {
			ASCLogger.error(this.getClass(), methodName,
					"No categories defined for managedServerName:"
							+ StepLogger.getManagedServerName());
			// This exception should terminate deployment
			throw new PRCMessageContainerException(
					"No categories defined for managedServerName:"
							+ StepLogger.getManagedServerName());
		} else {
			messageContainersLst = new ArrayList<DefaultMessageListenerContainer>();
			for (int iCount = 0; iCount < taskCategoryInfoLst.size(); iCount++) {
				CfgPrcToTaskCategory prcToTaskCategoryMap = taskCategoryInfoLst
						.get(iCount);
				this.instantiateContainer(prcToTaskCategoryMap);
			}
			isRunning = true;
		}
	}

	/**
	 * Instantiates default message listener container
	 * 
	 * @param taskCategoryInfo
	 */
	private void instantiateContainer(CfgPrcToTaskCategory prcToTaskCategoryMap) {
		String methodName = "instantiateContainer";
		ASCLogger.info(this.getClass(), methodName,
				"Instantiating Message Container with Prc Category Info:"
						+ prcToTaskCategoryMap);
		DefaultMessageListenerContainer messageContainer = new DefaultMessageListenerContainer();
		messageContainer.setConnectionFactory(connectionFactory);
		messageContainer.setDestination(this
				.createDestination(prcToTaskCategoryMap
						.getCfgTaskCategoryInfo().getQueueName()));
		messageContainer.setMessageListener(messageListener);
		messageContainer.setSessionTransacted(sessionTransacted);
		if (sessionTransacted) {
			messageContainer.setTransactionManager(transactionManager);
		}
		messageContainer.setConcurrentConsumers(prcToTaskCategoryMap
				.getConcurrentConsumers());
		messageContainer.setMaxConcurrentConsumers(prcToTaskCategoryMap
				.getMaxConcurrentConsumers());
		messageContainer.setTaskExecutor(taskExecutor);
		// After properties set makes call to the initialize method to start the
		// container
		messageContainer.afterPropertiesSet();
		messageContainersLst.add(messageContainer);
	}

	/**
	 * The method creates a destination object from the supplied jndi name
	 * 
	 * @param jndiName
	 * @return
	 */
	private Destination createDestination(String jndiName) {
		String methodName = "createDestination";
		JndiObjectFactoryBean jndiFactoryBean = new JndiObjectFactoryBean();
		jndiFactoryBean.setJndiTemplate(jndiTemplate);
		jndiFactoryBean.setJndiName(jndiName);
		try {
			jndiFactoryBean.afterPropertiesSet();
		} catch (IllegalArgumentException iaException) {
			ASCLogger.error(this.getClass(), methodName,
					"Failed to call after properties set to create destination for jndiName:"
							+ jndiName, iaException);
			throw new PRCMessageContainerException(
					"All properties not set for getting destination from jndiName:"
							+ jndiName, iaException);
		} catch (NamingException namingException) {
			ASCLogger.error(this.getClass(), methodName,
					"Failed to lookup, jndiName:" + jndiName, namingException);
			throw new PRCMessageContainerException("Failed to lookup jndiName:"
					+ jndiName, namingException);
		}
		jndiFactoryBean.setExpectedType(Destination.class);
		return (Destination) jndiFactoryBean.getObject();
	}

	/**
	 * @param stepLogger
	 *            the stepLogger to set
	 */
	public void setStepLogger(StepLogger stepLogger) {
		ASCLogger
				.debug(
						this.getClass(),
						"setStepLogger",
						"The setter needs to be called just to ensure that step logger object has already been instantiated");
	}

	/**
	 * @param prcToTaskCategoryDAO
	 *            the prcToTaskCategoryDAO to set
	 */
	public void setPrcToTaskCategoryDAO(
			IPrcToTaskCategoryDAO prcToTaskCategoryDAO) {
		this.prcToTaskCategoryDAO = prcToTaskCategoryDAO;
	}

	/**
	 * @param jndiTemplate
	 *            the jndiTemplate to set
	 */
	public void setJndiTemplate(JndiTemplate jndiTemplate) {
		this.jndiTemplate = jndiTemplate;
	}

	/**
	 * @param transactionManager
	 *            the transactionManager to set
	 */
	public void setTransactionManager(
			PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	/**
	 * @param sessionTransacted
	 *            the sessionTransacted to set
	 */
	public void setSessionTransacted(boolean sessionTransacted) {
		this.sessionTransacted = sessionTransacted;
	}

	/**
	 * @param messageListener
	 *            the messageListener to set
	 */
	public void setMessageListener(Object messageListener) {
		this.messageListener = messageListener;
	}

	/**
	 * @param connectionFactory
	 *            the connectionFactory to set
	 */
	public void setConnectionFactory(ConnectionFactory connectionFactory) {
		this.connectionFactory = connectionFactory;
	}

	/**
	 * @param taskExecutor
	 *            the taskExecutor to set
	 */
	public void setTaskExecutor(TaskExecutor taskExecutor) {
		this.taskExecutor = taskExecutor;
	}

	/**
	 * @see org.springframework.context.Lifecycle#isRunning()
	 */
	public boolean isRunning() {
		ASCLogger.debug(this.getClass(), "isRunning", "isRunning:" + isRunning);
		return isRunning;
	}

	/**
	 * @see org.springframework.context.Lifecycle#start()
	 */
	public void start() {
		if (!this.isRunning()) {
			ASCLogger.info(this.getClass(), "start",
					"Factory was dead so restarting, Skipping...");
			this.doInitialize();
		} else {
			ASCLogger
					.info(this.getClass(), "start",
							"Factory already started so no need to restart, Skipping...");
		}
	}

	/**
	 * @see org.springframework.context.Lifecycle#stop()
	 */
	public void stop() {
		String methodName = "stop";
		ASCLogger.info(this.getClass(), methodName,
				"Stopping all message containers, in total:"
						+ messageContainersLst.size());

		for (int iCount = 0; iCount < messageContainersLst.size(); iCount++) {
			try {
				ASCLogger.info(this.getClass(), methodName,
						"Stopping message container with queue name:"
								+ messageContainersLst.get(iCount)
										.getDestination());
				messageContainersLst.get(iCount).stop(null);

			} catch (RuntimeException runtimeException) {
				ASCLogger
						.info(this.getClass(), methodName,
								"Failed to shutdown message container for queue:"
										+ messageContainersLst.get(iCount)
												.getDestinationName(),
								runtimeException);
			}
		}
		messageContainersLst = new ArrayList<DefaultMessageListenerContainer>();
		isRunning = false;
		ASCLogger.info(this.getClass(), methodName,
				"Stopped all message container, isRunning:" + isRunning);
	}

}
